package org.xi2p;

import java.io.PrintWriter;
import java.util.ArrayList;

public class core {
    public static final double LINE_LENGTH = 4000.0;
    public static final double WIDTH = 854.0;
    public static final double HEIGHT = 481.0;
    public static final double NOTE_WIDTH = 110.0;
    public static final double NOTE_HEIGHT = 12.0;
    public static final double NOTE_R = Math.pow(
            Math.pow(NOTE_HEIGHT/2, 2) + Math.pow(NOTE_WIDTH/2, 2), 0.5
    );
    public static final double NOTE_THETA = Math.toDegrees(
            Math.atan(NOTE_HEIGHT/NOTE_WIDTH)
    );
    public static final double BAR_WIDTH = 22;
    public static double KEYBOARD_OFFSET = 0;
    public static double PERFECT = 0.2;
    public static double GOOD = 0.4;
    public static long commandNum = 0;
    public static long noteNum = 0;
    public static long missNum = 0;
    public static long goodNum = 0;
    public static long perfectNum = 0;
    public static long badNum = 0;
    public static int score = 0;
    public static int combo = 0;
    public static int maxCombo = 0;
    public static PrintWriter printWriter;
    public static BadPainter badPainter;

    public static ArrayList<Judgeline> judgeline_list = new ArrayList<>();

    public static Respond respond;

    public static void respond(String command){
//        System.out.println("-> " + command);
        commandNum++;
        String[] blocks = command.split(" ");

        switch (blocks[0]) {
            case "init" -> {
                // command format: init mode bpm keyboard_offset
                commandNum = 0;
                noteNum = 0;
                missNum = 0;
                goodNum = 0;
                perfectNum = 0;
                badNum = 0;
                score = 0;
                combo = 0;
                maxCombo = 0;
                judgeline_list.clear();

                if (blocks[1].equals("autoplay")) {
                    respond = new RespondAutoplay();
                } else {
                    respond = new RespondUserplay();
                }

                double bpm = Double.parseDouble(blocks[2]);
                PERFECT =  0.08 * bpm / 60.0;
                GOOD =  0.16 * bpm / 60.0;
                KEYBOARD_OFFSET = Double.parseDouble(blocks[3]);
            }
            case "cj" ->
                // cj -> create judgeline
                    judgeline_list.add(new Judgeline());
            case "jx" ->
                // jx -> judgeline x motion
                // command: jx key target origin endBeat startBeat
                    judgeline_list.get(judgeline_list.size() - 1).xObject.addEvent(
                            Integer.parseInt(blocks[1]),
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            Double.parseDouble(blocks[5])
                    );
            case "jy" ->
                // judgeline y motion
                // command:  jy key target origin endBeat startBeat
                    judgeline_list.get(judgeline_list.size() - 1).yObject.addEvent(
                            Integer.parseInt(blocks[1]),
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            Double.parseDouble(blocks[5])
                    );
            case "ja" ->
                // judgeline alpha motion
                // command:  ja key target origin endBeat startBeat
                    judgeline_list.get(judgeline_list.size() - 1).alphaObject.addEvent(
                            Integer.parseInt(blocks[1]),
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            Double.parseDouble(blocks[5])
                    );
            case "jd" ->
                // judgeline degree motion
                // command:  jd key target origin endBeat startBeat
                    judgeline_list.get(judgeline_list.size() - 1).angleObject.addEvent(
                            Integer.parseInt(blocks[1]),
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            Double.parseDouble(blocks[5])
                    );
            case "jny" ->
                // judgeline speed motion
                // command:  js key target origin endBeat startBeat
                    judgeline_list.get(judgeline_list.size() - 1).noteYObject.addEvent(
                            Integer.parseInt(blocks[1]),
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            Double.parseDouble(blocks[5])
                    );
            case "an" -> {
                // add note
                // command: an type x {1 if self.above else 0} alpha at end {1 if self.fake else 0} speed
                noteNum++;
                int type = Integer.parseInt(blocks[1]);
                Note note = switch (type) {
                    case 1 -> new Tap(
                            judgeline_list.get(judgeline_list.size() - 1),
                            type,
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[5]),
                            Double.parseDouble(blocks[6]),
                            "1".equals(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            "1".equals(blocks[7]),
                            Double.parseDouble(blocks[8])
                    );
                    case 2 -> new Drag(
                            judgeline_list.get(judgeline_list.size() - 1),
                            type,
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[5]),
                            Double.parseDouble(blocks[6]),
                            "1".equals(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            "1".equals(blocks[7]),
                            Double.parseDouble(blocks[8])
                    );
                    case 3 -> new Flick(
                            judgeline_list.get(judgeline_list.size() - 1),
                            type,
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[5]),
                            Double.parseDouble(blocks[6]),
                            "1".equals(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            "1".equals(blocks[7]),
                            Double.parseDouble(blocks[8])
                    );
                    default -> new Hold(
                            judgeline_list.get(judgeline_list.size() - 1),
                            type,
                            Double.parseDouble(blocks[2]),
                            Double.parseDouble(blocks[5]),
                            Double.parseDouble(blocks[6]),
                            "1".equals(blocks[3]),
                            Double.parseDouble(blocks[4]),
                            "1".equals(blocks[7]),
                            Double.parseDouble(blocks[8])
                    );
                };
                note.id = commandNum;

                if (note.type != 4) {
                    // 非 HOLD
                    judgeline_list.get(judgeline_list.size() - 1).notHolds.add(note);
                } else {
                    judgeline_list.get(judgeline_list.size() - 1).holds.add(note);
                }

                if ("1".equals(blocks[3])) {
                    judgeline_list.get(judgeline_list.size() - 1).above1.add(note);
                } else {
                    judgeline_list.get(judgeline_list.size() - 1).above2.add(note);
                }
            }
            case "ei" -> {
                // eval image

                if (score == 1000000){
                    printWriter.write("phi");
                } else if (score >= 950000){
                    if (noteNum == combo){
                        printWriter.write("vfc");
                    } else {
                        printWriter.write("v");
                    }
                } else if (score >= 920000){
                    printWriter.write("s");
                } else if (score >= 880000){
                    printWriter.write("a");
                } else if (score >= 820000){
                    printWriter.write("b");
                } else if (score >= 700000){
                    printWriter.write("c");
                } else {
                    printWriter.write("f");
                }
                printWriter.write("$");
                printWriter.flush();
            }
            case "shutdown" ->
                System.exit(0);

            default -> respond.respond(command);
        }
    }

}
